package com.niren.relay.netty;

/*import java.util.HashMap;
import java.util.Map;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;*/

import com.niren.relay.common.constant.constant;
import com.niren.relay.common.devinfo.GetDevState;
import com.niren.relay.common.portocol.AT.AtCmdGenerater;
import com.niren.relay.common.queues.AtomQueue;
import com.niren.relay.entity.SysDeviceManage;
import com.niren.relay.redis.RedisString;
import com.niren.relay.redis.formatjson.String2Json;
import com.sun.javafx.scene.control.skin.KeystrokeUtils;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;

import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

/**
 * 目前重连还会提示异常
 * 在类前面加上 @ChannelHandler.Sharable 解决重连接问题
 * */
@ChannelHandler.Sharable
public class TcpServerHandler extends ChannelInboundHandlerAdapter {
	/**
     * 存储客户端的信息表，以ctx为索引
	 * @key ctx
     * @value ClientState
	 * */
    Map<String,ClientState> ctxMap = new HashMap<String,ClientState>();
    /**
     * 存储客户端的信息表，以mac为索引
     * @key mac
     * @value ClientState
     * */
    Map<String,ClientState> macMap = new HashMap<String,ClientState>();

    public Map<String,ClientState> getctxMap(){
        return this.ctxMap;
    }

    public Map<String,ClientState> getMacMap(){
        return this.macMap;
    }

    int connectId = 0;
    int offlineId = 0;
    int state = 1;
    AtCmdGenerater atCmd = new AtCmdGenerater();
    
    public AtomQueue<DeviceMessage> recvMsgQueue = new AtomQueue<DeviceMessage>();
    
    /**
     * 读取数据
     * */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        //将客户端传入的消息转换为Netty的ByteBuf类型
        ByteBuf in = (ByteBuf) msg;
        // 在控制台打印传入的消息
        System.out.println(new Date() + "  -->  Server received: " + in.toString(CharsetUtil.UTF_8));
        
        if(this.ctxMap.containsKey(getRemoteAddress(ctx)) == false) {
        	ctx.close();//如果不存在Map中，即关闭
        	return ;
        }

        //协议处理
        ProtocolProcessing(ctx,in.toString(CharsetUtil.UTF_8));
    }
    
    private void ProtocolProcessing(ChannelHandlerContext ctx,String cmdStr) {
        //获取当前的远程地址
        String remoteAddress = getRemoteAddress(ctx);
        RedisString rString = new RedisString(constant.hostOrIp);
        String macsStr = rString.getValue(constant.macsKey);
        String2Json<SysDeviceManage> sdmJson = new String2Json<SysDeviceManage>();

    	//粘包处理，需要 进行分包
    	String[] cmdArr = cmdStr.split("\r");

    	//index<cmdArr.length-1  这里再减1，是因为最后一个字符串为" \n "
    	for(int index =0;index<cmdArr.length-1;index++) {
    		
    		if(cmdArr[index].contains("+WID") == true) {//读ID，处理
    			/*String deviceId = GetAtValue(cmdArr[index]);
    			
    			//这一句已经将ID与ctx进行绑定
    			if(this.ctxMap.containsKey(deviceId) == false) 
    	            ctxMap.put(deviceId,new ClientState(){
    	                @Override
    	                public void setCtx(ChannelHandlerContext ctx) {
    	                    super.setCtx(ctx);
    	                    super.setLastOnlineTime(new Date());
    	                }
    	            });

    			ctxMap.get(deviceId).setDeviceId(deviceId);
    	        this.ctxMap.get(deviceId).lastOnlineTime = new Date();
    	        ctxMap.get(deviceId).setCommStatus(1);*///在线
    	        
    			//向数据库中写入该ID的在线状态 ，注意，状态 为在线
    			
    		}else if(cmdArr[index].contains("+STACH") == true) {//读输出状态处理
    			String outState = GetAtValue(cmdArr[index]);//获得输出端口状态
                setOnlineInfo(remoteAddress);
                UpdateRtValue(remoteAddress,cmdArr[index],sdmJson,"+STACH");
    		}else if(cmdArr[index].contains("+OCCH") == true) {//读输入状态处理
    			String inState = GetAtValue(cmdArr[index]);//获得输入端口状态
                setOnlineInfo(remoteAddress);
    			//如果需要将此状态显示给用户，请将些状态更新到对应的地方，数据库或内存
                UpdateRtValue(remoteAddress,cmdArr[index],sdmJson,"+OCCH");
    		} else if(cmdArr[index].contains("AT") == true) {
    			//this.SendMsg(ctx, "AT+ACK\r\n");//应答硬件的心跳
                GetDevState getDevState = new GetDevState();
                getDevState.setCtx(this.ctxMap.get(remoteAddress).getCtx());
                getDevState.setCmd(AtCmdGenerater.GetAtAck());
                constant.getStateQueue.InQueue(getDevState);

                setOnlineInfo(remoteAddress);
    		} else if(cmdArr[index].contains("+MAC") == true){
                //向数据库中写入该ID的在线状态 ，注意，状态 为在线
                String mac = GetAtValue(cmdArr[index]);

                if(macsStr.contains(mac) == false) { //如果不存在此mac，即直接关闭
                    this.ctxMap.get(remoteAddress).closeClient();
                    this.ctxMap.remove(remoteAddress);
                    break;
                }

                this.ctxMap.get(remoteAddress).setMac(mac);
                setOnlineInfo(remoteAddress);

                this.macMap.put(mac,this.ctxMap.get(remoteAddress));
                UpdateOutState(remoteAddress);
                UpdateInState(remoteAddress);
                /*constant.setStateQueue.InQueue();
                this.SendMsg(ctx,AtCmdGenerater.GetOutStateCmd(0));*/
            }

    	}
    	
    }

    public void UpdateRtValue(
            String remoteAddress,
            String cmd,
            String2Json<SysDeviceManage> sdmJson,
            String subCmd){

        int len = subCmd.length();
        int startIndex = cmd.indexOf(subCmd) + len;
        int totalLen = cmd.length();
        int endIndex = cmd.indexOf(":");

        /*String ch = cmd.substring(cmd.indexOf(subCmd)+subCmd.length(),
                cmd.length()-(cmd.indexOf(subCmd)+subCmd.length()));*/
        String ch = cmd.substring(startIndex,endIndex);
        String val = cmd.split("\\:")[1];
        if(val.contains(",")){
            val = val.split(",")[0];
        }
        //如果需要将此状态显示给用户，请将些状态更新到对应的地方，数据库或内存
        String key = "";//macMap.get(ctxMap.get(remoteAddress).getMac()) + "|0|" + ch;
        if(subCmd.contains("STACH")){
            key = ctxMap.get(remoteAddress).getMac() + "|0|" + ch;
        } else{
            key = ctxMap.get(remoteAddress).getMac() + "|1|" + ch;
        }
        SysDeviceManage sdm = sdmJson.Json2Class(key,SysDeviceManage.class);
        sdm.setRtValue(Integer.parseInt(val));

        sdmJson.Class2Json(key,sdm);

    }

    public void UpdateOutState(String remoteAddress){
        UpdateState(remoteAddress,1);
    }

    public void UpdateInState(String remoteAddress){
        UpdateState(remoteAddress,0);
    }

    public void UpdateState(String remoteAddress,int chType){
        String mac = this.ctxMap.get(remoteAddress).getMac();
        RedisString rString = new RedisString(constant.hostOrIp);
        HashSet<String> keys = rString.getKeys("4E*");
        String ch = "0";
        for(String key: keys){

            if(key.contains(mac + "|" + chType) == false) continue;
            ch = key.split("\\|")[2];
            GetDevState getDevState = new GetDevState();
            ChannelHandlerContext ctx = this.ctxMap.get(remoteAddress).getCtx();
            getDevState.setCtx(ctx);
            if(chType == 0)
                getDevState.setCmd(AtCmdGenerater.GetOutStateCmd(Integer.parseInt(ch)));
            else
                getDevState.setCmd(AtCmdGenerater.GetInStateCmd(Integer.parseInt(ch)));

            constant.getStateQueue.InQueue(getDevState);
        }

    }

    private void setOnlineInfo(String remoteAddress){
        this.ctxMap.get(remoteAddress).lastOnlineTime = new Date();
        ctxMap.get(remoteAddress).setCommStatus(1);//在线
    }
    
    private String GetAtValue(String cmd) {
        /*String[] cmdArr = cmd.split(":");
    	return cmdArr[1];*///例如，设备发过来的指令为 -->    +WID:123456789  ，返回的内容为  123456789,即取冒号后面的子字符串
        String val = cmd.substring(cmd.indexOf(":")+1);
        return val;
    }

    private String GetChNum(String cmd){
        String cmdStr = cmd.split(":")[0];
        cmdStr = cmdStr.substring(cmdStr.indexOf("STACH") + 4);
        return cmdStr;
    }
    
    /**
 	*  发送控制cmd
     * */
    public void SendMsg(ChannelHandlerContext ctx ,String msg) {
    	
    	try {
			Thread.sleep(50);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    	ctx.channel().writeAndFlush(Unpooled.copiedBuffer(msg , CharsetUtil.UTF_8));
    }

    /**
     * 客户端连接回调
     * */
    @Override
    public void channelActive(ChannelHandlerContext ctx){
        //System.out.println("channelActive");//连接上提示

        //添加新的IP
        createNewDevice(ctx);
        
        //SendMsg(ctx,atCmd.GetQueryMacCmd());//连接上，需要询问设备ID，如果设备没应答，30秒后，由超时线程关闭连接

        GetDevState getDevState = new GetDevState();
        getDevState.setCtx(ctx);
        getDevState.setCmd(atCmd.GetQueryMacCmd());
        constant.getStateQueue.InQueue(getDevState);

        //这个println在发布前，需要屏蔽
        connectId ++;
        //if((connectId % 100) == 0)
            System.out.println( new Date() + "  -->  第" + connectId + "设备连接上！");

    }

    /**
     * 客户端主动离线回调
     * */
    @Override
    public void channelInactive(ChannelHandlerContext ctx){
        //System.out.println("channelInactive");//断开提示
        //删除断开的IP
    	if(ctxMap.containsKey(ctx.channel().remoteAddress().toString()) == true)
    		ctxMap.remove(ctx.channel().remoteAddress().toString());
        ctx.close();

        //这个println在发布前，需要屏蔽
        offlineId ++;
        //if((offlineId % 100) == 0)
            System.out.println( new Date() + " -->  第" + offlineId + "断开！");
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("exceptionCaught");
        //打印异常栈跟踪
        cause.printStackTrace();
        // 关闭该Channel
        ctx.close();
    }

    private void createNewDevice(ChannelHandlerContext ctx){
        ClientState clientState = new ClientState();
        clientState.setCtx(ctx);
        this.ctxMap.put(getRemoteAddress(ctx),clientState);
    }

    private void createNewDevice2Mac(ChannelHandlerContext ctx,String mac){
        ClientState clientState = new ClientState();
        clientState.setCtx(ctx);
        clientState.setMac(mac);
        this.macMap.put(mac,clientState);
    }

    public String getRemoteAddress(ChannelHandlerContext ctx){
        return ctx.channel().remoteAddress().toString();
    }

    private void setMac(String key,Map<String,ClientState> mcMap,String mac){
        mcMap.get(key).setMac(mac);
    }

    private void setOnlineState(ChannelHandlerContext ctx){
        String remoteAddress = getRemoteAddress(ctx);
        this.ctxMap.get(remoteAddress).setLastOnlineTime(new Date());
        String mac = this.ctxMap.get(remoteAddress).getMac();
        this.macMap.get(mac).setLastOnlineTime(new Date());
    }
}
